package com.ccrfid.rmc6.service.device;

import com.ccrfid.rmc6.appconfig.exception.pojo.AppException;
import com.ccrfid.rmc6.appconfig.exception.pojo.CustomErrors;
import com.ccrfid.rmc6.entity.asset.AssetEntity;
import com.ccrfid.rmc6.entity.device.*;
import com.ccrfid.rmc6.entity.personnel.PersonnelEntity;
import com.ccrfid.rmc6.entity.personnel.PersonnelView;
import com.ccrfid.rmc6.lbservice3.mq.MsgDispatcher;
import com.ccrfid.rmc6.lbservice3.pojo.msg.MsgLocateActive;
import com.ccrfid.rmc6.repository.asset.AssetRepository;
import com.ccrfid.rmc6.repository.device.CardActiveRepository;
import com.ccrfid.rmc6.repository.device.CardActiveViewRepository;
import com.ccrfid.rmc6.repository.device.CardRepository;
import com.ccrfid.rmc6.repository.device.CardViewRepository;
import com.ccrfid.rmc6.repository.personnel.PersonnelRepository;
import com.ccrfid.rmc6.repository.personnel.PersonnelViewRepository;
import com.ccrfid.rmc6.repository.search.SearchCriteria;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import javax.ws.rs.core.Response;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@Transactional
public class CardServiceImpl implements CardService {
    private static final Logger logger = LoggerFactory.getLogger(CardServiceImpl.class);
    @PersistenceContext
    private EntityManager entityManager;
    @Autowired
    private CardRepository cardRepository;
    @Autowired
    private CardViewRepository cardViewRepository;
    @Autowired
    private PersonnelViewRepository personnelViewRepository;
    @Autowired
    private PersonnelRepository personnelRepository;
    @Autowired
    private CardActiveRepository cardActiveRepository;
    @Autowired
    private CardActiveViewRepository cardActiveViewRepository;
    @Autowired
    private AssetRepository assetRepository;
    @Autowired
    private MsgDispatcher msgDispatcher;

    @Override
    public Page<CardView> getCardInfoPage(SearchCriteria criteria, Pageable pageable) {
        return cardViewRepository.search(criteria, pageable);
    }

    @Override
    public CardEntity getCardById(Long cardId) {
        return cardRepository.getById(cardId);
    }

    @Override
    public CardEntity getCardByCardNumber(Long cardNumber) {
        return cardRepository.getByCardNumber(cardNumber);
    }

    @Override
    public CardEntity getCardByHexNumber(String hexNumber) {
        return cardRepository.getAllByHexNumber(hexNumber);
    }

    @Override
    public CardEntity createCard(CardEntity entity) throws AppException {
        if (entity.getSn() != null && cardRepository.existsBySn(entity.getSn())) {
            throw new AppException(Response.Status.CONFLICT.getStatusCode(),
                    CustomErrors.sn_conflict.getCode(), CustomErrors.sn_conflict.getReason(), "");
        }
        if (entity.getCardNumber() == null && entity.getHexNumber() == null) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.param_is_null.getCode(), CustomErrors.param_is_null.getReason(), "");
        }
        if (entity.getHexNumber() == null) {
            entity.setHexNumber(Long.toHexString(entity.getCardNumber()).toUpperCase());
        } else {
            entity.setCardNumber(Long.parseLong(entity.getHexNumber(), 16));
            entity.setHexNumber(entity.getHexNumber().toUpperCase());
        }
        if (cardRepository.existsByCardNumber(entity.getCardNumber())) {
            throw new AppException(Response.Status.CONFLICT.getStatusCode(),
                    CustomErrors.card_number_conflict.getCode(), CustomErrors.card_number_conflict.getReason(), entity.getHexNumber());
        }
        return cardRepository.save(entity);
    }

    @Override
    public CardEntity updateCard(Long cardId, CardEntity entity) throws AppException {
        checkUpdateParam(cardId, entity);
        return cardRepository.save(entity);
    }

    @Override
    public CardEntity updateCard(CardEntity entity) {
        return cardRepository.save(entity);
    }

    private void checkUpdateParam(Long cardId, CardEntity entity) throws AppException {
        if (!cardId.equals(entity.getId())) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.different_args.getCode(), CustomErrors.different_args.getReason(), "");
        }
        if (entity.getSn() != null && cardRepository.existsBySnAndIdNot(entity.getSn(), cardId)) {
            throw new AppException(Response.Status.CONFLICT.getStatusCode(),
                    CustomErrors.sn_conflict.getCode(), CustomErrors.sn_conflict.getReason(), "");
        }
        if (cardRepository.existsByCardNumberAndIdNot(entity.getCardNumber(), cardId)) {
            throw new AppException(Response.Status.CONFLICT.getStatusCode(),
                    CustomErrors.card_number_conflict.getCode(), CustomErrors.card_number_conflict.getReason(), "");
        }
    }

    @Override
    public PersonnelView getCardBindPersonnelInfo(Long cardId) {
        CardEntity cardEntity = cardRepository.getById(cardId);
        if (cardEntity != null) {
            return personnelViewRepository.getByCardNumber(cardEntity.getCardNumber());
        }
        return null;
    }

    @Override
    public int deleteCards(List<Long> idList) {
        int count = 0;
        for (Long id : idList) {
            if (cardRepository.existsById(id)) {
                CardEntity cardEntity = cardRepository.getById(id);
                //1.清除人员绑定关系
                if (personnelRepository.existsByCardNumber(cardEntity.getCardNumber())) {
                    PersonnelEntity personnelEntity = personnelRepository.getByCardNumber(cardEntity.getCardNumber());
                    personnelEntity.setCardNumber(null);
                    personnelRepository.save(personnelEntity);
                    msgDispatcher.unBindPersonnelCard(personnelEntity.getId(), cardEntity.getCardNumber());
                }
                //2.清除资产绑定关系
                if (assetRepository.existsByCardNumber(cardEntity.getCardNumber())) {
                    AssetEntity assetEntity = assetRepository.getByCardNumber(cardEntity.getCardNumber());
                    assetEntity.setCardNumber(null);
                    assetRepository.save(assetEntity);
                }
                //3.清除当前定位
                if (cardActiveRepository.existsByCardNumber(cardEntity.getCardNumber())) {
                    cardActiveRepository.deleteByCardNumber(cardEntity.getCardNumber());
                }
                //4.删除标签
                if (cardRepository.existsById(id)) {
                    cardRepository.deleteById(id);
                }
                count++;
            }
        }
        return count;
    }

    @Override
    public List<CardActiveView> getAllCardActives() {
        return cardActiveViewRepository.getAllBy();
    }

    @Override
    public List<CardEntity> getAllCards() {
        return cardRepository.getAllBy();
    }

    @Override
    public CardActiveEntity updateCardActive(MsgLocateActive msgLocateActive) {
        CardActiveEntity cardActiveEntity = new CardActiveEntity();
        cardActiveEntity.setCardNumber(msgLocateActive.getCardNumber());
        cardActiveEntity.setLocatorAddress(msgLocateActive.getCurrentLocator());
        cardActiveEntity.setX(msgLocateActive.getX());
        cardActiveEntity.setY(msgLocateActive.getY());
        cardActiveEntity.setAccuracy(msgLocateActive.getAccuracy());
        cardActiveEntity.setFloorNumber(msgLocateActive.getCurrentFloorNumber());
        cardActiveEntity.setUpdateTime(new Timestamp(msgLocateActive.getTime()));
        return cardActiveRepository.save(cardActiveEntity);
    }

    @Override
    public CardActiveEntity getCardActive(Long cardNumber) {
        return cardActiveRepository.getByCardNumber(cardNumber);
    }

    @Override
    public Page<UntreatedCardDto> getCardStatisticPage(Map<String, Integer> param, Pageable pageable) {
        String sql = "";
        if (param.isEmpty()) {
            sql = "SELECT v.*,vc.locator_address as locator_address,vc.pos as pos,vc.floor_number as floor_number,vc.update_time as update_time" +
                    " FROM v_card v left join v_card_active vc" +
                    " on v.card_number = vc.card_number" +
                    " where v.lowpower_status = 1 or v.cutoff_status = 1 or v.online_status = 0 or vc.pos is null ";
        } else {
            StringBuilder stringBuilder = new StringBuilder("SELECT v.*,vc.locator_address as locator_address,vc.pos as pos,vc.floor_number as floor_number,vc.update_time as update_time" +
                    " FROM v_card v left join v_card_active vc" +
                    " on v.card_number = vc.card_number" +
                    " where 1");
            if (param.containsKey("onlineStatus")) {
                stringBuilder.append(" and v.online_status =" + param.get("onlineStatus"));
            }
            if (param.containsKey("cutoffStatus")) {
                stringBuilder.append(" and v.cutoff_status =" + param.get("cutoffStatus"));
            }
            if (param.containsKey("lowpowerStatus")) {
                stringBuilder.append(" and v.lowpower_status =" + param.get("lowpowerStatus"));
            }
            if (param.containsKey("pos") && param.get("pos") == 1) {
                stringBuilder.append(" and vc.pos is null");
            }
            sql = stringBuilder.toString();
        }
        String countSql = "select count(*) as total from (" + sql + ") as t";
        BigInteger total = (BigInteger) entityManager.createNativeQuery(countSql)
                .getSingleResult();
        List<UntreatedCardDto> list = entityManager.createNativeQuery(sql, UntreatedCardDto.class)
                .setFirstResult((int) pageable.getOffset())
                .setMaxResults(pageable.getPageSize())
                .getResultList();
        return new PageImpl<UntreatedCardDto>(list, pageable, total.longValue());
    }

    @Override
    public Page<CardView> getCardInfo(String sql, Pageable pageable) {

        String countSql = "select count(*) as total from (" + sql + ") as t";
        BigInteger total = (BigInteger) entityManager.createNativeQuery(countSql)
                .getSingleResult();

        List<CardView> content = entityManager.createNativeQuery(sql, CardView.class)
                .setFirstResult((int) pageable.getOffset())
                .setMaxResults(pageable.getPageSize())
                .getResultList();
        return new PageImpl<>(new ArrayList<>(content), pageable, total.longValue());
    }

    @Override
    public Page<CardView> getCardInfoHuaihua(String sql, List<Long> orgIdList, Pageable pageable) {

        String countSql = "select count(*) as total from (" + sql + ") as t";
        BigInteger total = (BigInteger) entityManager.createNativeQuery(countSql)
                .setParameter("orgIdList",orgIdList)
                .getSingleResult();

        List<CardView> content = entityManager.createNativeQuery(sql, CardView.class)
                .setParameter("orgIdList",orgIdList)
                .setFirstResult((int) pageable.getOffset())
                .setMaxResults(pageable.getPageSize())
                .getResultList();
        return new PageImpl<>(new ArrayList<>(content), pageable, total.longValue());
    }

    @Override
    public Integer getLowpower(int enabled, int lowPowerStatu, int bindStatus, Integer personnelType, List<Long> orgIdList) {

        return cardViewRepository.countAllByEnabledAndLowpowerStatusAndBindStatusAndPersonnelTypeAndOrgIdIn(1, 1,1, 101, orgIdList);
    }

    @Override
    public Integer getCutOff(int enabled, int cutOffStatu, int bindStatus, Integer personnelType, List<Long> orgIdList) {
        return cardViewRepository.countAllByEnabledAndCutoffStatusAndBindStatusAndPersonnelTypeAndOrgIdIn(1, 1,1, 101, orgIdList);
    }

    @Override
    public Integer getOffline(int enabled, int onlineStatu, int bindStatus, Integer personnelType, List<Long> orgIdList) {
        return cardViewRepository.countAllByEnabledAndOnlineStatusAndBindStatusAndPersonnelTypeAndOrgIdIn(1, 0,1, 101, orgIdList);
    }
}
